using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Automation;
using Citrix.XaAdminMobile.Util;
using System.Reflection;
using System.ComponentModel;
using System.IO;

namespace Citrix.XaAdminMobile.MSRA
{
    /// <summary>
    /// This class is used to launch the Microsoft remote assist application for the specified
    /// user session. It uses UI automation to inject the user's session state into the remote
    /// assist app so the user doesn't have to enter it manually.
    /// </summary>
    internal static class RemoteAssist
    {
        /// <summary>
        /// Launches the remote assist app for the specified session.
        /// </summary>
        /// <param name="sessionDetails">Holds the session details to be injected into the app.</param>
        /// <exception cref="ArgumentNullException">Thrown if the session details are null.</exception>
        public static void LaunchMsra(SessionDetails sessionDetails)
        {
            if (null == sessionDetails)
            {
                throw new ArgumentNullException("sessionDetails");
            }

            System.Threading.ThreadPool.QueueUserWorkItem(wi =>
                {
                    Process process = null;
                    try
                    {
                        process = StartMSRAProcess(sessionDetails);
                        if (null != process)
                        {
                            Thread.Sleep(500);
                            DriveMSRA(sessionDetails);
                        }

                    }
                    catch (Win32Exception ex)
                    {
                        FTrace.Except(MethodBase.GetCurrentMethod(), ex);
                        App.TypedApp.Dispatch(() => PopupHelper.ShowAcknowledgePopup("Failed to start Remote Assistance:", ex.Message));
                    }
                    catch (InvalidOperationException ex)
                    {
                        FTrace.Except(MethodBase.GetCurrentMethod(), ex);
                    }
                    catch (FileNotFoundException ex)
                    {
                        FTrace.Except(MethodBase.GetCurrentMethod(), ex);
                    }
                    catch (RemoteAssistException ex)
                    {
                        FTrace.Except(MethodBase.GetCurrentMethod(), ex);

                        if (null != process)
                        {
                            try
                            {
                                process.Kill();
                            }
                            catch (Win32Exception)
                            { }
                            catch (NotSupportedException)
                            { }
                            catch (InvalidOperationException)
                            { }
                        }

                        // Show the error to the user.
                        App.TypedApp.Dispatch(() => PopupHelper.ShowAcknowledgePopup("Remote Assist Error:", ex.Message));
                    }
                });
        }

        /// <summary>
        /// Launches the remote assist process, and pre-injects the target server name via the
        /// command line.
        /// </summary>
        /// <param name="sessionDetails">Session details - we use the server name.</param>
        /// <returns>The process object.</returns>
        private static Process StartMSRAProcess(SessionDetails sessionDetails)
        {
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = "MSRA.EXE";
            startInfo.Arguments = "/offerra " + sessionDetails.ServerName;
            return Process.Start(startInfo);
        }

        /// <summary>
        /// Looks for the remote assistance app window and tries to select the specified user's
        /// session.
        /// </summary>
        /// <param name="sessionDetails"></param>
        private static void DriveMSRA(SessionDetails sessionDetails)
        {
            // get the desktop element
            AutomationElement aeDesktop = AutomationElement.RootElement;

            // Get the main form for the MSRA app
            AutomationElement aeForm = null;
            int numWaits = 0;
            do
            {
                aeForm = aeDesktop.FindFirst(TreeScope.Children,
                    new PropertyCondition(AutomationElement.NameProperty, "Windows Remote Assistance"));
                FTrace.Trace5(MethodBase.GetCurrentMethod(), "Looking for Windows Remote Assistance . . . ");
                ++numWaits;
                Thread.Sleep(100);
            }
            while (aeForm == null && numWaits < 50);

            if (aeForm == null)
            {
                throw new RemoteAssistException("Failed to find the Windows Remote Assistance application.");
            }

            FTrace.Trace5(MethodBase.GetCurrentMethod(), "Enumerating the controls tree to find all relevant objects");

            PropertyCondition propCond1 = new PropertyCondition(AutomationElement.NameProperty, "Windows Remote Assistance");
            PropertyCondition propCond2 = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Pane);
            PropertyCondition propCond3 = new PropertyCondition(AutomationElement.LocalizedControlTypeProperty, "dialog", PropertyConditionFlags.IgnoreCase);
            AndCondition propCondFinal = new AndCondition(propCond1, propCond2, propCond3);

            AutomationElement aeDlg = aeForm.FindFirst(TreeScope.Children, propCondFinal);

            if (aeDlg == null)
            {
                throw new RemoteAssistException("Failed to find Windows Remote Assistance dialog.");
            }

            // get session list combox box
            AutomationElement aeSessionsList = aeDlg.FindFirst(TreeScope.Children,
                new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ComboBox));

            if (aeSessionsList == null)
            {
                throw new RemoteAssistException("Failed to find the Windows Remote Asistance session dialog.");
            }

            SelectListItem(aeSessionsList, sessionDetails);
        }

        /// <summary>
        /// Selects the specified user in the remote assistance dialog.
        /// </summary>
        /// <param name="element"></param>
        /// <param name="sessionDetails"></param>
        private static void SelectListItem(AutomationElement element, SessionDetails sessionDetails)
        {
            // Get the session list combobox
            AutomationElement comboboxList = element.FindFirst(TreeScope.Children,
                new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.List));

            // Expand & collapse the lsit so tht it gets filled in
            ExpandCollapsePattern expandPattern = (ExpandCollapsePattern)element.GetCurrentPattern(ExpandCollapsePattern.Pattern);
            expandPattern.Expand();
            expandPattern.Collapse();

            // Find the item that matches the session of interest
            string sessionName = sessionDetails.DomainName + "\\" + sessionDetails.UserName + ":" + sessionDetails.SessionID;
            AutomationElement listItem = comboboxList.FindFirst(TreeScope.Children,
                new PropertyCondition(AutomationElement.NameProperty, sessionName, PropertyConditionFlags.IgnoreCase));

            if (listItem != null)
            {
                SelectionItemPattern selectionItemPattern;
                selectionItemPattern = (SelectionItemPattern)listItem.GetCurrentPattern(SelectionItemPattern.Pattern);
                selectionItemPattern.Select();
            }
            else
            {
                throw new RemoteAssistException("Couldn't find the user session in the Remote Assistance app: " + sessionName);
            }
        }
    }
} 
